/*! Copyright (c) 2012 Bobby Chan (http://bobbychanblog.com)
 * Licensed under the MIT License (LICENSE.txt).
 *
 * Thanks to: Brandom Aaron for his jquery mousewheel plugin
 *
 * Version: 0.0.7
 * 
 * Requires: jquery.mousewheel.js (http://brandonaaron.net/code/mousewheel/docs)
 * 			 jquery-ui.js (http://jqueryui.com/)
 */
(function($) {
	$.fn.iscrollbar = function(options) {
		var defaults = {
			hoverOffTime: 200,
			vscroll: true,
			hscroll: false,
			edgeOffset: 5, 
			thickness: 10,
			scrollOffset: -1,
			color: "#000",
			opacity: 0.65,
			inner: null,	// The inner content to scroll.	
			onScrollStart: function(){},
			onScrollStop: function(){},
			onScrollDrag: function(){}
		};

		var scrolling = true;
		
		var options = $.extend(defaults, options);
		
		// html objects
		var scrollable,	// The window div which contains the scrollable div.
			inner, // The inner scrollable div. 
			hbar,
			vbar;
		
		/*
		 * mouseWheelUp
		 *  - delta: percentage of page to scroll in decimal format (ie 0.1 for scroll 10%)
		 * If vertical scrolling is enabled, then scroll up.
		 * If vertical scrolling is disabled, but horizontal scrolling is enabled,
		 * then scroll to the left.
		 */
		function mouseWheelUp(delta) {
			// Check if vertical scrolling is enabled.
			if(options.vscroll) {
				var offset = inner.height()*delta;
				// If forced scrollOffset is enabled,
				// force it to scroll the number of pixels 
				// specified in the settings.
				if(options.scrollOffset != -1) {
					offset = options.scrollOffset;
				}
				
				var newInnerPos = (inner.position().top+offset);
				// Prevent inner div from flowing outside the containment div
				if(newInnerPos > 0) {
					newInnerPos = 0;
				} else if(newInnerPos < -(inner.height()-scrollable.height())) {
					newInnerPos = -(inner.height()-scrollable.height());
				}
				// Move the inner scrollable div
				inner.css("top", newInnerPos+"px");
				// Move the scrollbar
				vbar.css("top", -(scrollable.height()/inner.height()*newInnerPos)+"px");
	
			} else if(options.hscroll) {
				
				var offset = inner.width()*delta;
				if(options.scrollOffset != -1) {
					offset = options.scrollOffset;
				}
				var newInnerPos = (inner.position().left+offset);
				if(newInnerPos > 0) {
					newInnerPos = 0;
				} else if(newInnerPos < -(inner.width()-scrollable.width())) {
					newInnerPos = -(inner.width()-scrollable.width());
				}
				inner.css("left", newInnerPos+"px");
				hbar.css("left", -(scrollable.width()/inner.width()*newInnerPos)+"px");
			}
		}
		
		/*
		 * mouseWheelDown
		 *  - delta: percentage of page to scroll in decimal format (ie 0.1 for scroll 10%)
		 * If vertical scrolling is enabled, the scroll down.
		 * If vertical scrolling is disabled, but horizontal scrolling is enabled,
		 * then scroll to the right.
		 */
		function mouseWheelDown(delta) {
			if(options.vscroll) {
				var offset = inner.height()*delta;
				if(options.scrollOffset != -1) {
					offset = options.scrollOffset;
				}
				
				// Move inner div
				var newInnerPos = (inner.position().top-offset);
				if(newInnerPos > 0) {
					newInnerPos = 0;
				} else if(newInnerPos < -(inner.height()-scrollable.height())) {
					newInnerPos = -(inner.height()-scrollable.height());
				}
				inner.css("top", newInnerPos+"px");
				
				// Move scrollbar
				var newbarPos = -(scrollable.height()/inner.height()*newInnerPos);
				
				var barLowLimit = scrollable.height()-vbar.height()-10;
				if(newbarPos > barLowLimit) {
					newbarPos = barLowLimit;
				}
				vbar.css("top", newbarPos+"px");
			} else if(options.hscroll) {
				
				var offset = inner.width()*delta;
				if(options.scrollOffset != -1) {
					offset = options.scrollOffset;
				}
				
				// Move inner div
				var newInnerPos = (inner.position().left-offset);
				if(newInnerPos > 0) {
					newInnerPos = 0;
				} else if(newInnerPos < -(inner.width()-scrollable.width())) {
					newInnerPos = -(inner.width()-scrollable.width());
				}
				inner.css("left", newInnerPos+"px");
				
				// Move scrollbar
				var newbarPos = -(scrollable.width()/inner.width()*newInnerPos);
				var barRightLimit = scrollable.width()-hbar.width()-10;
				if(newbarPos > barRightLimit) {
					newbarPos = barRightLimit;
				}
				hbar.css("left", newbarPos+"px");
			}
		}
		
		/*
		 * resizeScrollbar()
		 * Called when the brower's viewport is resized. Adjustments to the scrollbar's
		 * size will adjust accordingly.
		 */
		function resizeScrollbar() {
			var hScrollbarWidth = scrollable.width() * scrollable.width()/inner.width();
			var vScrollbarHeight = scrollable.height() * scrollable.height()/inner.height();
			if(options.vscroll) {
				vbar.css({
					height: vScrollbarHeight+"px"
				});
			}
			if(options.hscroll) {
				hbar.css({
					width: hScrollbarWidth+"px"
				});
			}
		}
		
		/*
		 * isScrollable()
		 * Determine's if the inner div is large enough for the scrollable 
		 * container div to require scrolling.
		 */
		function isScrollable() {
			if(options.hscroll && (inner.width() <= scrollable.width())) {
				return false;
			}
			
			if(options.vscroll && (inner.height() <= scrollable.height())) {
				return false;
			}
			
			return true;
		}
		
		function setupScrollbars() {
			
			var hScrollbarWidth = scrollable.width() * scrollable.width()/inner.width();
			var vScrollbarHeight = scrollable.height() * scrollable.height()/inner.height();
			
			// Set CSS properties.
			scrollable.css({
				position: "relative",
				overflow: "hidden"
			});
			
			inner.css("position", "relative");
			
			if(options.vscroll) {
				scrollable.append('<div class="ivbar"></div>');
				vbar = scrollable.find('div.ivbar');
				vbar.css({
					position: "absolute",
					width: options.thickness+"px",
					height: vScrollbarHeight+"px",
					background: options.color,
					opacity: "0",
					zIndex: 9999,
					borderRadius: "5px",
					right: 0,
					top: 0,
					margin: options.edgeOffset+"px",
					cursor: "pointer"
				});
			}
			
			if(options.hscroll) {
				scrollable.append('<div class="ihbar"></div>');
				hbar = scrollable.find('div.ihbar');
				hbar.css({
					position: "absolute",
					width: hScrollbarWidth+"px",
					height: options.thickness+"px",
					background: options.color,
					opacity: "0",
					zIndex: 9999,
					borderRadius: "5px",
					bottom: 0,
					margin: options.edgeOffset+"px",
					cursor: "pointer"
				});
			}
			
			scrollable.hover(function(){
				if(isScrollable())  {
					$(this).find(".ivbar, .ihbar").css({
						opacity: options.opacity,
						display: "block"
					});
				}
			}, function() {
				if(!scrolling) {
					$(this).find(".ivbar, .ihbar").stop().fadeOut(options.hoverOffTime, function(){});		
				}
			});
			
			if(vbar) {
				vbar.draggable({
					containment: 'parent',
					axis: 'y',
					start: function() {
						scrolling = true;
						options.onScrollStart();
					},
					drag: function(e, ui) {
						var barPos = vbar.position().top;
						var innerPos = -Math.round(barPos/scrollable.height()*inner.height());
						if(options.scrollOffset != -1) {
							innerPos = Math.round(innerPos/options.scrollOffset)*options.scrollOffset;
						}
						inner.css({
							top: innerPos+"px"
						});
						options.onScrollDrag();
					},
					stop: function() {
						scrolling = false;
						vbar.stop().fadeOut(options.hoverOffTime, function(){});
						options.onScrollStop();
					}
				});
			}
			
			if(hbar) {
				hbar.draggable({
					containment: 'parent',
					axis: 'x',
					start: function() {
						if(isScrollable()) {
							scrolling = true;
							options.onScrollStart();
						}
					},
					drag: function(e, ui) {
						if(isScrollable()){
							var barPos = hbar.position().left;
							var innerPos = -Math.round(barPos/scrollable.width()*inner.width());
							if(options.scrollOffset != -1) {
								innerPos = Math.round(innerPos/options.scrollOffset)*options.scrollOffset;
							}
							
							inner.css({
								left: innerPos+"px"
							});
							// Execute callback function
							options.onScrollDrag();
						}
					},
					stop: function() {
						if(isScrollable()) {
							scrolling = false;
							hbar.stop().fadeOut(options.hoverOffTime, function(){});
							options.onScrollStop();
						}
					}
				});
			}
			
			scrollable.mousewheel(function(event) {
				var delta = 0;
				if (!event) /* For IE. */
						event = window.event;
				if (event.originalEvent.wheelDelta) { /* IE/Opera. */
						delta = event.originalEvent.wheelDelta/120;
						/** In Opera 9, delta differs in sign as compared to IE.
						 */
						if (window.opera)
								delta = -delta;
				} else if (event.detail) { /** Mozilla case. */
						/** In Mozilla, sign of delta is different than in IE.
						 * Also, delta is multiple of 3.
						 */
						delta = -event.detail/3;
				}
				/** If delta is nonzero, handle it.
				 * Basically, delta is now positive if wheel was scrolled up,
				 * and negative, if wheel was scrolled down.
				 */
				if (delta) {
					// Mousewheel up.
					if(delta > 0) {
						if(isScrollable()) {
							options.onScrollDrag();
							// Scroll through 10% of inner div
							mouseWheelUp(0.1);
						}
					// Mousewheel down.
					} else {
						if(isScrollable()) {
							options.onScrollDrag();
							// Scroll through 10% of inner div
							mouseWheelDown(0.1);
						}
						
					}
				}
				/** Prevent default actions caused by mouse wheel.
				 * That might be ugly, but we handle scrolls somehow
				 * anyway, so don't bother here..
				 */
				event.returnValue = false;
				scrolling = false;
				// Supress mousewheel scrolling on window
				return false;
			});
		}
		
		return this.each(function() {
			
			if(typeof $.ui == 'undefined') {
				console.error("Warning! jquery.ui plugin not detected.");
			}
			if(typeof $.fn.mousewheel == 'undefined') {
				console.error("Warning! jquery.mousewheel plugin not detected.");
			}
			
			scrollable = $(this);
			inner = (options.inner != null)? options.inner : scrollable.find(">div:first-child")
			
			// Setup scrollbars
			setupScrollbars();
			
			// Resize scrollbar when window is resized.
			$(window).resize(function(){
				resizeScrollbar();
			});
		});
	};
})(jQuery);